# define ORSQK or_ed_select_query_keywords

// Returns the required regex to find out the part
// between any two full words, i.e. if <before> is
// "x" and <after> is "y", it will return a pattern
// which will recognize "x abc... " when supplied
// the string "x abc... y" but nothing when given
// "zx abc... y" since "zx" != "x".
// Note that if <after> is a blank string - which
// is the default - it is implied that we want both
// <before> and <after> to be the same string.
RE in_between( const string& before, const string& after = blank ){

	// Ensures that the whole word is matched.
	static const string ww = "\\b(?<![(\\?])";

	if( after == blank ) return RE( "\\s*("+ww+before+"\\s+.*?)(("+ww+before+"\\s+)|$)", icase );
	else return RE( "\\s*("+ww+before+"\\s+.*?)(("+ww+after+"\\s+)|$)", icase );

}

pair<strings,strings> handle_aliases( const string& s ){

	strings t_oname, t_alias;

 	strings temp_pairs = tokenize( s, true, false, "," );
 	for( unsigned i = 0, t = temp_pairs.size(); i < t; i++ ){

 		// Possible Name(s) of Tables
 		strings pn_tables = tokenize( temp_pairs.at( i ), true, false, "{( )};" );

 		if( pn_tables.size() == 2 ){
			t_oname.push_back( to_upper_copy( pn_tables.at(0) ) );
			t_alias.push_back( to_upper_copy( pn_tables.at(1) ) );
		}

		else if( pn_tables.size() == 1 ){
			t_oname.push_back( to_upper_copy( pn_tables.at(0) ) );
			// As such we internally make no
			// assumptions about the table's
			// alternative names and keep an
			// empty string (or a sentinel).
			t_alias.push_back( blank );
		}

		// Such cases must be serious(?)
		// anomalies in the supplied log.
		else{ if( pn_tables.size() == 0 ) cerr << "<1";
		else cerr << ">2"; cerr << "# " << s << "\n\n"; }

 	}

 	return make_pair( t_oname, t_alias );

}

void separate_counts( string& statement, const string& current_database,
		pair<strings,strings> overall_table_list ){

	string general_portion;
	static const RE general_portion_re = in_between( ORSQK );
	for( int i = 0; general_portion_re.PartialMatch( statement, &general_portion ); i++ ){

		replace_first( statement, general_portion, "" );

		// Unnecessary (those which contain no column names
		// of interest) components must be ignored (skipped).
		trim( general_portion ); // ..to remove any (trailing) whitespace.
		pair<unsigned,string> result = individualize( general_portion );
		if( result.first > 1 ) continue;

		int map_ref;

		string clause_s = blank;
		switch( result.second.at(0) ){

			case 's': map_ref = CNAME_MAP; break;
			case 'w': map_ref = RCOUNT_MAP; break;
			case 'g': map_ref = CNAME_MAP; break;
			case 'o': map_ref = CNAME_MAP; break;

			case 'j':

				static const RE joinOnRegEx2 = in_between( string( "J#O#I#N#ON" ), ORSQK );
				joinOnRegEx2.PartialMatch( general_portion, &clause_s );
				count( clause_s, current_database, overall_table_list, JCLAUSE_MAP );

				map_ref = SKIP_MAP; break;

			default: map_ref = SKIP_MAP; break;

		}

		if( map_ref != SKIP_MAP )
			count( general_portion, current_database, overall_table_list, map_ref );

	}

}
